// Author: Ivan Chau
package uk.ac.cam.kcic2.scene;

import java.util.ArrayList;

import uk.ac.cam.kcic2.math.Matrix4;
import uk.ac.cam.kcic2.math.Rotation;
import uk.ac.cam.kcic2.math.Scale;
import uk.ac.cam.kcic2.math.Transform;
import uk.ac.cam.kcic2.math.Vector3D;

public abstract class SceneObject {

	protected final Matrix4 localToParentPointTransform;
	protected final Matrix4 localToParentNormalTransform;
	protected final Matrix4 parentToLocalPointTransform;
	protected final Matrix4 parentToLocalNormalTransform;
	protected final Scale localToParentScaleTransform;

	protected final Vector3D centre = new Vector3D(0,0,0);
	protected final Vector3D normal = new Vector3D(0,0,1);
	// all SceneObjects are centred at origin with its normal being the z-axis
	// in their local coordinates
	public final ColourCoeffs colourCoeffs;

	protected CompoundObject parent;

	public SceneObject(ArrayList<Transform> trans, ColourCoeffs coeffs, CompoundObject father){
		colourCoeffs = coeffs;

		if (trans != null){
			Matrix4 pointParentTransform = Transform.IDENTITY;
			Matrix4 normalParentTransform = Transform.IDENTITY;
			Matrix4 pointLocalTransform = Transform.IDENTITY;
			Matrix4 normalLocalTransform = Transform.IDENTITY;
			Scale scaleTransform = new Scale(1,1,1);

			for (Transform t : trans){
				pointParentTransform = pointParentTransform.mult(t);
				pointLocalTransform = t.inverse().mult(pointLocalTransform);
				// only rotation and scale affects normals
				if (Rotation.class.isInstance(t)){
					normalParentTransform = normalParentTransform.mult(t);
					normalLocalTransform = t.inverse().mult(normalLocalTransform);
				}
				else if (Scale.class.isInstance(t)){
					normalParentTransform = normalParentTransform.mult(t.inverse());
					normalLocalTransform = t.inverse().mult(normalLocalTransform);
					scaleTransform = scaleTransform.mult((Scale)t);
				}

			}
			localToParentPointTransform = pointParentTransform;
			localToParentNormalTransform = normalParentTransform;
			parentToLocalPointTransform = pointLocalTransform;
			parentToLocalNormalTransform = normalLocalTransform;

			localToParentScaleTransform = scaleTransform;
		}
		else{
			localToParentPointTransform = Transform.IDENTITY;
			localToParentNormalTransform = Transform.IDENTITY;
			parentToLocalPointTransform = Transform.IDENTITY;
			parentToLocalNormalTransform = Transform.IDENTITY;
			localToParentScaleTransform = new Scale(1,1,1);
		}

		parent = father;
	}

	public abstract IntersectionInfo getIntersection(Ray ray);
	// return information about the intersecting point 
	// between the ray and the object

	protected double minT(double[] tParams){
		// return the min value that is > 0
		double min = Double.POSITIVE_INFINITY;
		for (int i = 0; i < tParams.length; i++){
			if (tParams[i] > 0 && min > tParams[i]){
				min = tParams[i];
			}
		}
		return min;
	}

}
